home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 May: Tool Chest / Dev.CD May 97 TC.toast / Sample Code / Toolbox / MacCalendar 1.1b1 / DrawCalendar.c next >
Encoding:
Text File  |  1997-03-20  |  10.0 KB  |  333 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        DrawCalendar.c
  3.  
  4.     Contains:    Module for displaying the calendar (Gregorian).
  5.  
  6.     Written by:    Martin Minow
  7.  
  8.     Copyright:    © 1994-1997 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.     You may incorporate this sample code into your applications without
  13.     restriction, though the sample code has been provided "AS IS" and the
  14.     responsibility for its operation is 100% yours.  However, what you are
  15.     not permitted to do is to redistribute the source as "DSC Sample Code"
  16.     after having made changes. If you're going to re-distribute the source,
  17.     we require that you make it clear in the source that the code was
  18.     descended from Apple Sample Code, but that you've made changes.
  19.  
  20.     DrawCalendar displays the calendar (Gregorian) for the selected date. The
  21.     algorithm has been simplified and consequently will only work for dates within
  22.     the Macintosh epoch.
  23.  
  24.     Note that DrawCalendar is common to the Control Strip and Setup application.
  25.     If you change it, you must rebuild both modules.
  26. */
  27.  
  28. /////////////////////////////////////////////////////////////////////////
  29.  
  30. // Pick up some common stuff, specifically the height and width macros.
  31.  
  32. #include "MacCalendarCommon.h"
  33.  
  34. /////////////////////////////////////////////////////////////////////////
  35.  
  36. // Pick up prototypes for our exported routines.
  37.  
  38. #include "DrawCalendar.h"
  39.  
  40. /////////////////////////////////////////////////////////////////////////
  41.  
  42. // Pick up system types.
  43.  
  44. #include <Fonts.h>
  45. #include <IntlResources.h>
  46. #include <Memory.h>
  47. #include <OSUtils.h>
  48. #include <Packages.h>
  49. #include <QuickDraw.h>
  50. #include <Script.h>
  51. #include <TextUtils.h>
  52.  
  53. /////////////////////////////////////////////////////////////////////////
  54.  
  55. // Some global constants.
  56.  
  57. #define    kFebruary        2                        /* The magic month                    */
  58.  
  59. /*
  60.  * This character vector contains the number of days in a month. Because compilers
  61.  * do not necessarily allow pc-relative addressing of generic vectors, we define
  62.  * it as a character string.
  63.  *  034        28
  64.  *    035        29
  65.  *    036        30
  66.  *    037        31
  67.  */                    /*    Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec            */
  68. #define kDayInMonth "\000\037\034\037\036\037\036\037\037\036\037\036\037"
  69.  
  70. /////////////////////////////////////////////////////////////////////////
  71.  
  72. static short GetFontNumber(
  73.         SavedSettingsHandle        settings        /* Current font etc            */
  74.     )
  75. {
  76.         Str255                    fontName;
  77.         short                    fontNumber;
  78.  
  79.         pstrcpy(fontName, (**settings).fontName);
  80.         GetFNum(fontName, &fontNumber);
  81.  
  82.         return (fontNumber);
  83. }
  84.  
  85. /////////////////////////////////////////////////////////////////////////
  86.  
  87. // GetCalendarDisplaySize
  88.  
  89. // Return the width and height of a rectangle needed to display the calendar in the
  90. // specified font and font size. This is needed to draw the calendar and to position
  91. // the calendar display with respect to the Control Strip.
  92.  
  93. Point                        GetCalendarDisplaySize(
  94.         SavedSettingsHandle        settings        /* Current font etc            */
  95.     )
  96. {
  97.         FontInfo                fontInfo;
  98.         short                    dateWidth;
  99.         short                    lineHeight;
  100.         Point                    result;
  101.         short                    saveTextFont;
  102.         short                    saveTextSize;
  103.         short                    saveTextFace;
  104.         GrafPtr                    currentPort;
  105.         
  106.         GetPort(¤tPort);
  107.         saveTextFont = currentPort->txFont;
  108.         saveTextSize = currentPort->txSize;
  109.         saveTextFace = currentPort->txFace;
  110.         TextFont(GetFontNumber(settings));
  111.         TextSize((**settings).fontSize);
  112.         TextFace(normal);
  113.         GetFontInfo(&fontInfo);
  114.         lineHeight = fontInfo.ascent + fontInfo.descent + fontInfo.leading;
  115.         dateWidth = StringWidth("\p 00");
  116.         result.h = (dateWidth * 7) + 4;        /* 7 == days in the week                */
  117.         result.v = (lineHeight * 8) + 4;    /* 8 == lines of text in the calendar    */
  118.         TextFont(saveTextFont);
  119.         TextSize(saveTextSize);
  120.         TextFace(saveTextFace);
  121.         return (result);
  122. }
  123.  
  124. /////////////////////////////////////////////////////////////////////////
  125.  
  126. // GetCalendarMonthRect
  127. //
  128. // Given a display rectangle, font, and font size, create the actual display area.
  129.  
  130. void                        GetCalendarMonthRect(
  131.         SavedSettingsHandle        settings,        /* Current font etc            */
  132.         const Rect                *displayRect,    /* Where to draw the text    */
  133.         Rect                    *monthRect        /* Returns drawing rect        */
  134.     )
  135. {
  136.         Point                    monthSize;
  137.         
  138.         monthSize = GetCalendarDisplaySize(settings);
  139.         /*
  140.          * Center the month rectangle within the drawing rectangle.
  141.          * >> 1 is used to divide by two without loading a library routine.
  142.          */
  143.         monthRect->left = displayRect->left
  144.                     + ((width(*displayRect) - monthSize.h) >> 1);
  145.         monthRect->top = displayRect->top
  146.                     + ((height(*displayRect) - monthSize.v) >> 1);
  147.         monthRect->right = monthRect->left + monthSize.h;
  148.         monthRect->bottom = monthRect->top + monthSize.v;
  149. }
  150.  
  151. /////////////////////////////////////////////////////////////////////////
  152.  
  153. // DrawCalendar
  154. //
  155. // Draw the month - the port is set to the drawing port. Text font, size, and style
  156. // are not preserved. dayName is a Pascal string with the following format, repeated
  157. // seven times, once for each day:
  158. //    { nByte, Byte1, Byte2, etc }. For example, if dates are represented
  159. // by "S M Tu W Th F S", dayName would be specified in a pascal string as
  160. //        "\p\001S\001M\002Tu\001W\002Th\001F\001S\000"
  161. // Note: the day names must correspond to the firstDayOfWeek parameter. I.e. if the
  162. // first day is Monday, the first word in the string is "M".
  163.  
  164. void                        DrawCalendar(
  165.         SavedSettingsHandle        settings,        /* Current font etc            */
  166.         short                    year,            /* 1904 ..                    */
  167.         short                    month,            /* January == 1                */
  168.         const Rect                *displayRect    /* Where to draw the text    */
  169.     )
  170. {
  171.         DateTimeRec                now;
  172.         unsigned long            nowSeconds;
  173.         short                    weekday;
  174.         short                    daysInMonth;
  175.         short                    today;
  176.         short                    lineHeight;
  177.         short                    dateWidth;
  178.         short                    spaceWidth;
  179.         short                    digitWidth;
  180.         FontInfo                fontInfo;
  181.         Rect                    monthRect;
  182.         short                    hPos;
  183.         short                    vPos;
  184.         register unsigned char    *dayNamePtr;
  185.         short                    dayWidth;
  186.         Intl1Hndl                intlHdl;
  187.         Str255                    work;
  188.         Boolean                    isThisMonth;
  189.         short                    thisDate;
  190.         Rect                    dayRect;
  191.         PenState                penState;
  192.         short                    hOffset;
  193.         short                    vOffset;
  194.         short                    penSize;
  195.         
  196.         /*
  197.          * Get the drawing parameters for this month. This duplicates the logic of
  198.          * GetCalendarDisplaySize above.
  199.          */
  200.         GetCalendarMonthRect(settings, displayRect, &monthRect);
  201.         TextFont(GetFontNumber(settings));
  202.         TextSize((**settings).fontSize);
  203.         TextFace(normal);
  204.         GetFontInfo(&fontInfo);
  205.         lineHeight = fontInfo.ascent + fontInfo.descent + fontInfo.leading;
  206.         spaceWidth = CharWidth(' ');
  207.         digitWidth = CharWidth('0');
  208.         dateWidth = spaceWidth + (digitWidth * 2);
  209.         /*
  210.          * If we're displaying the current month, we want to hilite today's date.
  211.          * 1.0d3
  212.          */
  213.         GetDateTime(&nowSeconds);
  214.         SecondsToDate(nowSeconds, &now);
  215.         isThisMonth = (year == now.year && month == now.month);
  216.         thisDate = now.day;
  217.         /*
  218.          * Get the parameters for this particular month. We convert day 1 to seconds,
  219.          * then back to the date in order to locate the weekday corresponding to the
  220.          * first day of the month.
  221.          */
  222.         now.year = year;
  223.         now.month = month;
  224.         now.day = 1;
  225.         now.hour = 0;
  226.         now.minute = 0;
  227.         now.second = 0;
  228.         DateToSeconds(&now, &nowSeconds);
  229.         SecondsToDate(nowSeconds, &now);
  230.         vPos = monthRect.top + 2 + fontInfo.ascent;
  231.         /*
  232.          * Draw the month and year names.
  233.          */
  234.         intlHdl = (Intl1Hndl) GetIntlResource(1);
  235.         if (intlHdl != NULL) {
  236.             /*
  237.              * 1.0d4: Don't modify the actual data.
  238.              */
  239.             if (HandToHand((Handle *) &intlHdl) == noErr) {
  240.                 /*
  241.                  * Convert the date to "month, year" (myd + supress day)
  242.                  */
  243.                 (**intlHdl).suppressDay = 3;
  244.                 IUDatePString(nowSeconds, myd, work, (Handle) intlHdl);
  245.                 hPos = monthRect.left + 2
  246.                     + ((width(monthRect) - StringWidth(work)) >> 1);
  247.                 MoveTo(hPos, vPos);
  248.                 DrawString(work);
  249.                 vPos += lineHeight;
  250.             }
  251.             DisposeHandle((Handle) intlHdl);
  252.         }    
  253.         /*
  254.          * Draw the days in the week. dayName is a vector of Pascal strings hiding
  255.          * inside a Pascal string.
  256.          */
  257.         pstrcpy(work, (**settings).dayNameString);
  258.         dayNamePtr = (unsigned char *) &work[1];
  259.         hPos = monthRect.left + 2;
  260.         TextFace(bold);                                        /* 1.0d3                */
  261.         for (weekday = 0; weekday < 7; weekday++) {
  262.             dayWidth = StringWidth((StringPtr) dayNamePtr);
  263.             MoveTo(hPos + dateWidth - dayWidth, vPos);
  264.             DrawString((StringPtr) dayNamePtr);
  265.             dayNamePtr += dayNamePtr[0] + 1;
  266.             hPos += dateWidth;
  267.         }
  268.         TextFace(normal);                                    /* 1.0d3                */
  269.         vPos += lineHeight;
  270.         /*
  271.          * How far do we go in this month, with a leap year hack.
  272.          */
  273.         daysInMonth = kDayInMonth[month];
  274.         if ((year & 0x3) == 0 && month == kFebruary)
  275.             ++daysInMonth;
  276.         /*
  277.          * now.dayOfWeek is the weekday corresponding to the first day of the month.
  278.          * For example, if the first day of the month is on a Sunday, now.dayOfWeek
  279.          * will equal one. firstDayOfWeek will equal one for Sunday, two for Monday.
  280.          */
  281.         weekday = now.dayOfWeek - (**settings).firstDayOfWeek;
  282.         if (weekday < 0)
  283.             weekday = 6;
  284.         hPos = monthRect.left + 2 + (weekday * dateWidth);
  285.         for (today = 1; today <= daysInMonth; today++, weekday++) {
  286.             if (weekday >= 7) {        /* Wrap around to a new week.                    */
  287.                 hPos = monthRect.left + 2;
  288.                 vPos += lineHeight;
  289.                 weekday = 0;
  290.             }
  291.             /*
  292.              * hOffset locates the left edge of the date -- this will cover one
  293.              * digit for dates 1 to 9, and two digits for 10 to 31.
  294.              */
  295.             hOffset = hPos + spaceWidth;
  296.             if (today < 10) {
  297.                 hOffset += digitWidth;        /* Space over the leftmost digit space    */
  298.                 MoveTo(hOffset, vPos);
  299.                 DrawChar(today + '0');
  300.             }
  301.             else {
  302.                 MoveTo(hOffset, vPos);
  303.                 DrawChar((today / 10) + '0');
  304.                 DrawChar((today % 10) + '0');
  305.             }
  306.             if (isThisMonth && today == thisDate) {            /* 1.0d3                */
  307.                 /*
  308.                  * We are drawing an oval around this date. There is quite a bit of
  309.                  * eyeball adjustment that could be re-adjusted by someone with
  310.                  * more (or less) visual taste.
  311.                  */
  312.                 GetPenState(&penState);
  313.                 penSize = (fontInfo.ascent >= 12) ? fontInfo.ascent / 6 : 1;
  314.                 vOffset = vPos - fontInfo.ascent - penSize;
  315.                 PenSize(penSize, penSize);
  316.                 SetRect(
  317.                     &dayRect,
  318.                     hOffset - penSize,
  319.                     vOffset,
  320.                     hPos + dateWidth + penSize,
  321.                     vOffset + lineHeight + penSize
  322.                 );
  323.                 FrameRoundRect(
  324.                     &dayRect,
  325.                     (dateWidth * 3) / 4,
  326.                     (lineHeight * 3) / 4
  327.                 );
  328.                 SetPenState(&penState);
  329.             }
  330.             hPos += dateWidth;
  331.         }
  332. }
  333.